package com.zang.blogz.strategy.impl;

import cn.dev33.satoken.exception.DisableLoginException;
import cn.dev33.satoken.stp.SaLoginConfig;
import cn.dev33.satoken.stp.SaTokenInfo;
import cn.dev33.satoken.stp.StpUtil;
import com.zang.blogz.constant.ResultConstant;
import com.zang.blogz.dto.SocialTokenDTO;
import com.zang.blogz.dto.SocialUserInfoDTO;
import com.zang.blogz.enmus.LoginTypeEnum;
import com.zang.blogz.enmus.RoleEnum;
import com.zang.blogz.enmus.ZoneEnum;
import com.zang.blogz.entity.UserAuth;
import com.zang.blogz.entity.UserInfo;
import com.zang.blogz.entity.UserRole;
import com.zang.blogz.model.output.UserInfoVO;
import com.zang.blogz.result.Result;
import com.zang.blogz.service.UserAuthService;
import com.zang.blogz.service.UserInfoService;
import com.zang.blogz.service.UserRoleService;
import io.github.vampireachao.stream.core.optional.Opp;
import com.zang.blogz.strategy.SocialLoginStrategy;
import com.zang.blogz.utils.IpUtil;
import io.github.vampireachao.stream.plugin.mybatisplus.One;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;


import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @Author: ZVerify
 * @Description:
 * @DateTime: 2022/9/2 11:29
 **/
public abstract class AbstractSocialLoginStrategyImpl implements SocialLoginStrategy {

    @Autowired
    private UserAuthService userAuthService;

    @Autowired
    private UserInfoService userInfoService;

    @Autowired
    private UserRoleService userRoleService;

    @Autowired
    private HttpServletRequest request;

    @Override
    @Transactional
    public Result<UserInfoVO> login(String data, LoginTypeEnum loginTypeEnum) throws IOException {

        AtomicReference<Result<UserInfoVO>> result = new AtomicReference<>();
        // 创建登录信息
        SocialTokenDTO socialToken = getSocialToken(data);

        // 获取用户ip信息
        String ipAddress = IpUtil.getIpAddress(request);
        String ipSource = IpUtil.getIpSource(ipAddress);

        // 获取第三方用户信息
        SocialUserInfoDTO socialUserInfo = getSocialUserInfo(socialToken);
        // 判断是否已经注册
        Opp.of(userAuthService.getUserAuth(loginTypeEnum, socialUserInfo))

                .ifPresent((user) -> result.set(getUserDetail(user, ipAddress, ipSource, loginTypeEnum.getType())))
                .orElseRun(() -> {
                    try {
                        result.set(saveUserDetail(socialToken, ipAddress, ipSource, loginTypeEnum.getType(), socialUserInfo));
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                });

        Object loginId = StpUtil.getLoginId();
        if (StpUtil.isDisable(loginId)) {

            throw new DisableLoginException(loginTypeEnum.getDesc(), loginId, StpUtil.getDisableTime(loginId));
        }

        return result.get();

    }

    /**
     * 获取第三方token信息
     *
     * @param data 数据
     * @return {@link SocialTokenDTO} 第三方token信息
     */
    public abstract SocialTokenDTO getSocialToken(String data) throws IOException;

    /**
     * 获取第三方用户信息
     *
     * @param socialTokenDTO 第三方token信息
     * @return {@link SocialUserInfoDTO} 第三方用户信息
     */
    public abstract SocialUserInfoDTO getSocialUserInfo(SocialTokenDTO socialTokenDTO) throws IOException;


    /**
     * 获取用户信息
     *
     * @param user      用户账号
     * @param ipAddress ip地址
     * @param ipSource  ip源
     * @param loginType 登录类型
     * @return {@link UserInfoVO} 用户信息
     */
    private Result<UserInfoVO> getUserDetail(UserAuth user, String ipAddress, String ipSource, Integer loginType) {
        // 更新登录信息
        StpUtil.login(user.getUserInfoId(), SaLoginConfig
                .setExtra("name", user.getUsername())
                .setExtra("id", user.getUserInfoId())
                .setExtra("ipAddress", ipAddress)
                .setExtra("ipSource", ipSource)
        );
        // 返回信息

        SaTokenInfo tokenInfo = StpUtil.getTokenInfo();

        UserInfo userInfo = One.of(UserInfo::getId).eq(user.getUserInfoId()).query();

        return Result.ok(ResultConstant.LoginMessage.LOGIN_SUCCESS,
                UserInfoVO.builder()
                        .token(StpUtil.getTokenValue())
                        .webSite(userInfo.getWebSite())
                        .ipSource(user.getIpSource())
                        .ipSource(user.getIpSource())
                        .nickName(userInfo.getNickname())
                        .email(userInfo.getEmail())
                        .avatar(userInfo.getAvatar()).build());
    }

    /**
     * 新增用户信息
     *
     * @param socialToken token信息
     * @param ipAddress   ip地址
     * @param ipSource    ip源
     * @return {@link Result<UserInfoVO>} 用户信息
     */
    private Result<UserInfoVO> saveUserDetail(SocialTokenDTO socialToken, String ipAddress, String ipSource, Integer loginType, SocialUserInfoDTO socialUserInfo) throws IOException {

        // 保存用户信息
        UserInfo userInfo = UserInfo.builder()
                //
                .nickname(socialUserInfo.getNickname())
                .avatar(socialUserInfo.getAvatar())
                .build();

        userInfoService.save(userInfo);

        // 保存账号信息
        UserAuth userAuth = UserAuth.builder()
                .userInfoId(userInfo.getId())
                .username(userInfo.getNickname())
                .password(socialToken.getAccessToken())
                .loginType(loginType)
                .lastLoginTime(LocalDateTime.now(ZoneId.of(ZoneEnum.SHANGHAI.getZone())))
                .ipAddress(ipAddress)
                .ipSource(ipSource)
                .build();

        userAuthService.save(userAuth);

        // 绑定角色
        UserRole userRole = UserRole.builder()
                .userId(userInfo.getId())
                .roleId(RoleEnum.USER.getRoleId())
                .build();

        userRoleService.save(userRole);

        // 更新登录信息
        StpUtil.login(userAuth.getUserInfoId(), SaLoginConfig
                .setExtra("name", userAuth.getUsername())
                .setExtra("id", userAuth.getUserInfoId())
                .setExtra("ipAddress", ipAddress)
                .setExtra("ipSource", ipSource)
        );


        UserInfo user = One.of(UserInfo::getId).eq(userAuth.getUserInfoId()).query();

        return Result.ok(ResultConstant.LoginMessage.LOGIN_SUCCESS,
                UserInfoVO.builder()
                        .token(StpUtil.getTokenValue())
                        .webSite(user.getWebSite())
                        .ipSource(userAuth.getIpSource())
                        .ipSource(userAuth.getIpSource())
                        .nickName(user.getNickname())
                        .email(user.getEmail())
                        .avatar(user.getAvatar()).build());

    }
}
